package com.galen.manual.controller;

import com.galen.manual.common.Result;
import com.galen.manual.entity.FileRecord;
import com.galen.manual.service.FileRecordService;
import com.galen.manual.service.MinioService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

/**
 * 文件管理控制器
 */
@Slf4j
@Tag(name = "文件管理", description = "文件上传下载相关接口")
@RestController
@RequestMapping("/files")
@RequiredArgsConstructor
public class FileController {

    private final MinioService minioService;
    private final FileRecordService fileRecordService;

    @Operation(summary = "下载文件", description = "根据文件对象名称下载文件")
    @GetMapping("/download/{objectName:.+}")
    public ResponseEntity<InputStreamResource> downloadFile(
            @Parameter(description = "文件对象名称", required = true)
            @PathVariable String objectName) {

        try {
            log.info("开始下载文件，对象名称: {}", objectName);

            // URL解码
            String decodedObjectName = URLDecoder.decode(objectName, StandardCharsets.UTF_8);

            // 检查文件是否存在
            if (!minioService.fileExists(decodedObjectName)) {
                log.warn("文件不存在，对象名称: {}", decodedObjectName);
                return ResponseEntity.notFound().build();
            }

            // 获取文件输入流
            InputStream inputStream = minioService.downloadFile(decodedObjectName);
            InputStreamResource resource = new InputStreamResource(inputStream);

            // 更新下载次数
            try {
                FileRecord fileRecord = fileRecordService.getFileRecordByObjectName(decodedObjectName);
                if (fileRecord != null) {
                    fileRecordService.updateDownloadCount(fileRecord.getId());
                }
            } catch (Exception e) {
                log.warn("更新下载次数失败，对象名称: {}", decodedObjectName, e);
            }

            // 设置响应头
            HttpHeaders headers = new HttpHeaders();

            // 从对象名称中提取文件名
            String fileName = decodedObjectName;
            if (decodedObjectName.contains("/")) {
                fileName = decodedObjectName.substring(decodedObjectName.lastIndexOf("/") + 1);
            }

            // 设置文件名（URL编码）
            String encodedFileName = java.net.URLEncoder.encode(fileName, StandardCharsets.UTF_8);
            headers.add(HttpHeaders.CONTENT_DISPOSITION,
                       "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + encodedFileName);

            // 根据文件扩展名设置Content-Type
            String contentType = getContentType(fileName);
            headers.add(HttpHeaders.CONTENT_TYPE, contentType);

            log.info("文件下载成功，对象名称: {}", decodedObjectName);
            return ResponseEntity.ok()
                    .headers(headers)
                    .contentLength(inputStream.available())
                    .contentType(MediaType.parseMediaType(contentType))
                    .body(resource);

        } catch (Exception e) {
            log.error("文件下载失败，对象名称: {}", objectName, e);
            return ResponseEntity.internalServerError().build();
        }
    }

    @Operation(summary = "获取文件预签名URL", description = "获取文件的临时访问URL，用于前端直接访问")
    @GetMapping("/presigned-url/{objectName:.+}")
    public Result<String> getPresignedUrl(
            @Parameter(description = "文件对象名称", required = true)
            @PathVariable String objectName,
            @Parameter(description = "过期时间（秒），默认3600秒")
            @RequestParam(defaultValue = "3600") Integer expiry) {

        try {
            log.info("获取文件预签名URL，对象名称: {}, 过期时间: {}秒", objectName, expiry);

            // URL解码
            String decodedObjectName = URLDecoder.decode(objectName, StandardCharsets.UTF_8);

            // 检查文件是否存在
            if (!minioService.fileExists(decodedObjectName)) {
                return Result.error("文件不存在");
            }

            // 生成预签名URL
            String presignedUrl = minioService.getPresignedUrl(decodedObjectName, expiry);

            log.info("预签名URL生成成功，对象名称: {}", decodedObjectName);
            return Result.success(presignedUrl);

        } catch (Exception e) {
            log.error("获取预签名URL失败，对象名称: {}", objectName, e);
            return Result.error("获取预签名URL失败: " + e.getMessage());
        }
    }

    @Operation(summary = "获取文件上传预签名URL", description = "获取用于前端直传的预签名上传URL")
    @GetMapping("/upload-presigned-url/{objectName:.+}")
    public Result<String> getUploadPresignedUrl(
            @Parameter(description = "文件对象名称", required = true)
            @PathVariable String objectName,
            @Parameter(description = "过期时间（秒），默认3600秒")
            @RequestParam(defaultValue = "3600") Integer expiry) {

        try {
            log.info("获取文件上传预签名URL，对象名称: {}, 过期时间: {}秒", objectName, expiry);

            // URL解码
            String decodedObjectName = URLDecoder.decode(objectName, StandardCharsets.UTF_8);

            // 生成上传预签名URL
            String uploadPresignedUrl = minioService.getPresignedUploadUrl(decodedObjectName, expiry);

            log.info("上传预签名URL生成成功，对象名称: {}", decodedObjectName);
            return Result.success(uploadPresignedUrl);

        } catch (Exception e) {
            log.error("获取上传预签名URL失败，对象名称: {}", objectName, e);
            return Result.error("获取上传预签名URL失败: " + e.getMessage());
        }
    }

    @Operation(summary = "检查文件是否存在", description = "检查指定对象名称的文件是否存在")
    @GetMapping("/exists/{objectName:.+}")
    public Result<Boolean> checkFileExists(
            @Parameter(description = "文件对象名称", required = true)
            @PathVariable String objectName) {

        try {
            log.info("检查文件是否存在，对象名称: {}", objectName);

            // URL解码
            String decodedObjectName = URLDecoder.decode(objectName, StandardCharsets.UTF_8);

            boolean exists = minioService.fileExists(decodedObjectName);

            log.info("文件存在性检查完成，对象名称: {}, 存在: {}", decodedObjectName, exists);
            return Result.success(exists);

        } catch (Exception e) {
            log.error("检查文件存在性失败，对象名称: {}", objectName, e);
            return Result.error("检查文件存在性失败: " + e.getMessage());
        }
    }

    /**
     * 根据文件扩展名获取Content-Type
     */
    private String getContentType(String fileName) {
        if (fileName == null) {
            return MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }

        String extension = "";
        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex > 0 && dotIndex < fileName.length() - 1) {
            extension = fileName.substring(dotIndex + 1).toLowerCase();
        }

        switch (extension) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "webp":
                return "image/webp";
            case "svg":
                return "image/svg+xml";
            case "pdf":
                return "application/pdf";
            case "txt":
                return "text/plain";
            case "html":
                return "text/html";
            case "css":
                return "text/css";
            case "js":
                return "application/javascript";
            case "json":
                return "application/json";
            case "xml":
                return "application/xml";
            case "zip":
                return "application/zip";
            case "doc":
                return "application/msword";
            case "docx":
                return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            case "xls":
                return "application/vnd.ms-excel";
            case "xlsx":
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case "ppt":
                return "application/vnd.ms-powerpoint";
            case "pptx":
                return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
            default:
                return MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }
    }
}